/*
* @author Gerald <gera2ld@163.com>
*/
window.Mine=(function(){
	function Mine(options){
		var t=this;
		t.options=options;
		t.container=options.container;
		t.container.classList.add('mine-game');
		t.container.onclick=t.container.oncontextmenu=t.click.bind(t);
		// 剩余的空格数，-1表示游戏未开始
		t.blanks=-1;
		t.total=0;
		t.control={
			setLevel:t.setLevel.bind(t),
			restart:t.restart.bind(t),
		};
		t.setLevel(options.level||0);
	}
	Mine.prototype={
		levels:[
			{width:10,height:10,mines:10},
			{width:16,height:16,mines:40},
			{width:30,height:16,mines:99},
		],
		setLevel:function(l) {
			this.level=l;
			this.level_data=this.levels[l];
			this.restart();
		},
		callback:function(cmd,args) {
			var f=this.options['cb_'+cmd];
			if(f) f.call(this,args);
		},
		click:function(e){
			e.preventDefault();
			if(this.blanks) {
				var i=Array.prototype.indexOf.call(this.items,e.target);
				if(i>=0) switch(e.type) {
					case 'click':
						if(this.blanks<0) this.newGame(i);
						if(this.data[i].open) {
							if(this.validate(i)) this.spread(i);
							else this.spark(i);
						} else this.open(i);
						break;
					case 'contextmenu':
						if(this.blanks<0) return;
						if(!this.data[i].open) {
							if(this.data[i].marked=!this.data[i].marked) {
								this.items[i].classList.add('mine');
								this.mine_left--;
							} else {
								this.items[i].classList.remove('mine');
								this.mine_left++;
							}
							this.callback('mine_left',this.mine_left);
						}
						break;
				}
			}
		},

		getSurroundings:function(o) {
			// 返回第i个格子周围一圈的格子序号，不包括自己
			function getRowItems(i) {
				// 返回以i为中心的i所在行的3个序号
				if(i!=o) s.push(i);
				if(i%width) s.push(i-1);
				if((i+1)%width) s.push(i+1);
			}
			var s=[],width=this.level_data.width;
			if(o>=width) getRowItems(o-width);
			getRowItems(o);
			if(o+width<this.total) getRowItems(o+width);
			return s;
		},
		newGame:function(current){
			// 以所选格子为中心的3*3的范围内都不能有雷
			var t=this,mine_left=t.level_data.mines,
					cell_left=t.total-9,
					row=Math.floor(current/t.level_data.width),
					col=current%t.level_data.width,
					i,item,exclude;
			t.blanks=t.total-t.level_data.mines;
			t.data=[];
			// 如果所选格子靠边或靠角则需要排除的格子会少一些
			// 用i来计数，靠2条边即靠角
			i=0;
			if(!row||row==t.level_data.height-1) i++;
			if(!col||col==t.level_data.width-1) i++;
			if(i--) {
				// 靠一条边时，有3个被排除的格子不存在，加回来
				cell_left+=3;
				if(i) {
					// 靠角时，有5个格子不存在，则再加个2
					cell_left+=2;
				}
			}
			// 生成雷的分布表
			for(i=0;i<t.total;i++) {
				// 相隔一行一列以内则排除
				exclude=Math.abs(Math.floor(i/t.level_data.width)-row)<=1
							&&Math.abs(i%t.level_data.width-col)<=1;
				item={
					mine:!exclude&&mine_left/cell_left>Math.random(),
					num:0,
				};
				t.data.push(item);
				if(item.mine) mine_left--;
				if(!exclude) cell_left--;
			}
			// 更新所有格子中的数字
			for(i=0;i<t.total;i++) if(t.data[i].mine)
				t.getSurroundings(i).forEach(function(i){t.data[i].num++;});
			t.callback('start');
			t.startTimer();
		},
		clearTimer:function(cb){
			if(this.timer) {
				clearInterval(this.timer);
				this.timer=null;
			}
			if(cb) this.callback('timer',0);
		},
		startTimer:function(){
			var time=0,t=this;
			t.clearTimer(true);
			t.timer=setInterval(function(){
				time++;
				t.callback('timer',time);
			},1000);
		},

		restart:function(){
			this.total=this.level_data.width*this.level_data.height;
			// 生成游戏界面
			var b=[],i,j;
			for(i=0;i<this.level_data.height;i++) {
				for(j=0;j<this.level_data.width;j++)
					b.push('<span class=close></span>');
				b.push('<br>');
			}
			this.container.innerHTML=b.join('');
			this.items=this.container.querySelectorAll('span');
			this.mine_left=this.level_data.mines;
			this.callback('mine_left',this.mine_left);
			this.blanks=-1;
			this.clearTimer(true);
			this.callback('ready');
		},
		validate:function(i){
			var wrong=0,r=0,t=this;
			if(t.data[i].open) {
				r=1;
				t.getSurroundings(i).forEach(function(i){
					if(!t.data[i].mine&&t.data[i].marked)	// 标错雷了
						wrong=1;
					if(t.data[i].mine^t.data[i].marked) r=0;
				});
				if(wrong) t.lose(-1);
			}
			return r;
		},
		spread:function(i){
			// 第i个格子为空，则翻开周围的格子
			this.getSurroundings(i).forEach(this.open.bind(this));
		},
		spark:function(i){
			// 第i个格子为数字，且周围有未标记的雷，则闪烁一下
			var t=this,on=false;
			function spark() {
				on=!on;
				t.getSurroundings(i).forEach(function(i){
					if(!t.data[i].open&&!t.data[i].marked) {
						var c=t.items[i].classList;
						if(on) c.add('spark'); else c.remove('spark');
					}
				});
			}
			spark();setTimeout(spark, 200);
		},
		open:function(i){
			if(!this.data[i].open&&!this.data[i].marked) {
				if(this.data[i].mine)
					// 点中雷了
					this.lose(i);
				else {
					// 翻开一个
					this.items[i].className='open';
					this.data[i].open=1;this.blanks--;
					if(this.data[i].num)
						// 有数字则显示数字
						this.items[i].innerHTML=this.data[i].num;
					else
						// 否则翻开周围的格子
						this.spread(i);
					if(!this.blanks) {
						// 剩余空格数为0，游戏结束
						this.callback('win');
						// 将所有没标出的雷标记出来
						for(i=0;i<this.total;i++)
							if(this.data[i].mine&&!this.data[i].marked)
								this.items[i].classList.add('mine');
						this.clearTimer();
						this.callback('mine_left',0);
					}
				}
			}
		},
		lose:function(e){
			if(e>=0)	// 点中雷
				this.items[e].className='wrong mine';
			this.blanks=0;
			this.callback('lose');
			this.clearTimer();
			for(var i=0;i<this.total;i++)
				if(!this.data[i].mine&&this.data[i].marked)
					// 标错雷
					this.items[i].className='wrong';
				else if(this.data[i].mine&&!this.data[i].marked)
					// 未标记的雷
					this.items[i].classList.add('mine');
		}
	};


	return function(options){
		var mine=new Mine(options);
		return mine.control;
	};
})();
